/*
 * DIM-SUM操作系统 - 内存访问失败处理
 *
 * Copyright (C) 2023 国科础石(重庆)软件有限公司
 *
 * 作者: Dong Peng <w-pengdong@kernelsoft.com>
 *
 * License terms: GNU General Public License (GPL) version 3
 *
 */

#include <dim-sum/errno.h>
#include <dim-sum/types.h>
#include <dim-sum/bug.h>
#include <dim-sum/mm_types.h>
#include <asm-generic/current.h>
#include <asm/csr.h>
#include <asm/memfault.h>
#include <dim-sum/sched.h>

/**
 * 内存访问失败处理函数表
 */
static mem_fault_func mem_fault_funtab[EXC_VECT_MAX] = {
	[0 ... EXC_VECT_MAX - 1] = mem_fault_unknown,
	[EXC_INST_MISALIGNED] = mem_fault_inst_misaligned,
	[EXC_INST_ACCESS] = mem_fault_inst_access,
	[EXC_LOAD_ACCESS] = mem_fault_load_access,
	[EXC_STORE_ACCESS] = mem_fault_store_access,
	[EXC_INST_PAGE_FAULT] = mem_fault_inst_page,
	[EXC_LOAD_PAGE_FAULT] = mem_fault_load_page,
	[EXC_STORE_PAGE_FAULT] = mem_fault_store_page,
};

int mem_fault_unknown(struct exception_spot *s, u64 fault_addr, unsigned long esr)
{
	BUG();
	return -EFAULT;
}

int mem_fault_translation(struct exception_spot *s, u64 fault_addr, unsigned long esr)
{
	return mem_fault_access(s, fault_addr, esr);
}

int mem_fault_inst_misaligned(struct exception_spot *s, u64 fault_addr, unsigned long esr)
{
	BUG();
	return -EFAULT;
}

int mem_fault_inst_access(struct exception_spot *s, u64 fault_addr, unsigned long esr)
{
	BUG();
	return -EFAULT;
}

int mem_fault_load_access(struct exception_spot *s, u64 fault_addr, unsigned long esr)
{
	BUG();
	return -EFAULT;
}

int mem_fault_store_access(struct exception_spot *s, u64 fault_addr, unsigned long esr)
{
	BUG();
	return -EFAULT;
}

int mem_fault_inst_page(struct exception_spot *s, u64 fault_addr, unsigned long esr)
{
	return mem_fault_translation(s, fault_addr, esr);
}

int mem_fault_load_page(struct exception_spot *s, u64 fault_addr, unsigned long esr)
{
	return mem_fault_translation(s, fault_addr, esr);
}

int mem_fault_store_page(struct exception_spot *s, u64 fault_addr, unsigned long esr)
{
	return mem_fault_translation(s, fault_addr, esr);
}

int mem_fault_access(struct exception_spot *s, u64 fault_addr, unsigned long esr)
{
	int ret = 0;
	/**
	 * 在用户态访问用户空间内存
	 */
	if (fault_addr < USER_VA_END)
		if (!(ret = user_vmem_fault(s, fault_addr, esr)))
			return ret;

	/**
	 * 在内核态访问用户空间内存出错
	 */
	if (!user_mode(s) && (ret = kernel_mode_fault(s, fault_addr, esr)))
		return 0;

	pr_info("mem_fault_access addr:%lx current user stack:%lx current user pc:%lx ######, USER_VA_END: %p\n",
		fault_addr, s->sp, s->sepc, USER_VA_END);
	dump_task_user_regs(s);
	BUG();
	return -EFAULT;
}

int mem_fault(struct exception_spot *s, u64 fault_addr, unsigned long esr)
{
	int ret;
	
	if (likely(s->sstatus & SR_SPIE))
		local_irq_enable();

	ret = mem_fault_funtab[SCAUSE_TO_ECODE(esr)](s, fault_addr, esr);
	if (ret) {
		pr_info("mem_fault addr:%lx current user stack:%lx current user pc:%lx\n", fault_addr, s->sp, s->sepc);
		dump_task_user_regs(s);
		BUG();
	}
	return ret;
}


int user_vmem_fault(struct exception_spot *s, u64 fault_addr, unsigned long esr)
{
	//printk("%s, __task[%s,%lu], addr:[%lx], write:[%d] read:[%d]\n", __func__, current->name, current, fault_addr, is_write_abort(esr), is_read_abort(esr));
	return vmem_fault(current_mem, fault_addr, esr, s);
}

int kernel_mode_fault(struct exception_spot *s, u64 fault_addr, unsigned long esr)
{
	printk("%s, task[%s,%lu], addr:[%lx], write:[%d]\n", __func__, current->name, current, fault_addr, is_write_abort(esr));
	BUG();
	return 0; //fixup_exception(s);
}